package com.takanafu.haru{
	import com.takanafu.haru.code.CodeSymbol;
	import com.takanafu.haru.core.CharacterType;
	import com.takanafu.haru.core.TokenType;
	
	import flash.utils.Dictionary;
	
	public class Lexer{
		private var reader:String;
		private var position:Number;
		private var length:Number;
		private var tok:int;
		private var val:Object;
		
		private static var reserved:Dictionary = new Dictionary();
		reserved["true"] = new int(TokenType.TRUE);
		reserved["false"] = new int(TokenType.FALSE);
		reserved["if"] = new int(TokenType.IF);
		reserved["else"] = new int(TokenType.ELSE);
		reserved["while"] = new int(TokenType.WHILE);
		reserved["fun"] = new int(TokenType.FUN);
		reserved["def"] = new int(TokenType.DEF);
		reserved["object"] = new int(TokenType.OBJECT);
		reserved["new"] = new int(TokenType.NEW);
		
		public function Lexer(r:String){
			reader = r;
			position = 0;
			length = r.length;
		}

		public function advance():Boolean{
			try{
				if(position < length){
					skipWhiteSpace();
					var c:Number = reader.charCodeAt(position++);
					
					if(isNaN(c) == false){
						switch(c){
							case CharacterType.PLUS:
							case CharacterType.MINUS:
							case CharacterType.MUL:
							case CharacterType.SCOL:
							case CharacterType.LKAKKO:
							case CharacterType.RKAKKO:
							case CharacterType.LNYORO:
							case CharacterType.RNYORO:
							case CharacterType.CONMA:
							case CharacterType.DOT:
								this.tok = c;
								break;
							case CharacterType.DEV:
								c = reader.charCodeAt(position++);
								if(isNaN(c) == false){
									if(c == CharacterType.DEV){
										skipLineComment();
										return advance();
									}else if(c == CharacterType.MUL){
										skipComment();
										return advance();
									}else{
										--position;
										tok = CharacterType.DEV;
									}
								}else{
									return false;
								}
								break;
							case CharacterType.DBLQ:
								lexString();
								tok = TokenType.STRING;
								break;
							case CharacterType.EQUAL:
								c = reader.charCodeAt(position++);
					
								if(isNaN(c) == false){
									if(c == CharacterType.EQUAL){
										this.tok = TokenType.EQ;
									}else{
										--position;
										this.tok = CharacterType.EQUAL;
									}
								}else{
									return false;
								}
								break;
							case CharacterType.EXC:
								c = reader.charCodeAt(position++);
					
								if(isNaN(c) == false){
									if(c == CharacterType.EQUAL){
										this.tok = TokenType.NE;
									}else{
										--position;
										this.tok = CharacterType.EXC;
									}
								}else{
									return false;
								}
								break;
							case CharacterType.LES:
								c = reader.charCodeAt(position++);
					
								if(isNaN(c) == false){
									if(c == CharacterType.EQUAL){
										this.tok = TokenType.LE;
									}else{
										--position;
										this.tok = CharacterType.LES;
									}
								}else{
									return false;
								}
								break;
							case CharacterType.GRE:
								c = reader.charCodeAt(position++);
					
								if(isNaN(c) == false){
									if(c == CharacterType.EQUAL){
										this.tok = TokenType.GE;
									}else{
										--position;
										this.tok = CharacterType.GRE;
									}
								}else{
									return false;
								}
								break;
							case CharacterType.AMP:
								c = reader.charCodeAt(position++);
					
								if(isNaN(c) == false){
									if(c == CharacterType.AMP){
										this.tok = TokenType.AND;
									}else{
										throw new Error("＆は使えません。");
									}
								}else{
									return false;
								}
								break;
							case CharacterType.TATE:
								c = reader.charCodeAt(position++);
					
								if(isNaN(c) == false){
									if(c == CharacterType.TATE){
										this.tok = TokenType.OR;
									}else{
										throw new Error("｜は使えません。");
									}
								}else{
									return false;
								}
								break;
							default:
								if(isDisit(c) == true){
									--position;
									lexDigit();
									tok = TokenType.INT;
								}else if(CharacterType.isIdentifierStart(c) == true){
									--position;
									lexSymbol();
								}else{
									throw new Error("数字じゃないです");
								}
								break;
						}
					}else{
						throw new Error("isNaNがtrueでした");
					}
				}else{
					return false;
				}
			}catch(e:Error){
				throw e;
			}
			return true;
		}
		
		public function token():int{
			return tok;
		}
		
		public function value():Object{
			return val;
		}
		
		
		private function lexString():void{
			var str:String = "";

			while(position < length){
				var c:Number = reader.charCodeAt(position++);
				
				if(isNaN(c) == false){
					if(c == CharacterType.DBLQ){
						break;
					}else if(c == CharacterType.ESC){
						c = reader.charCodeAt(position++);
					}
					str += String.fromCharCode(c);
				}else{
					throw new Error("isNaNがtrueでした");
				}
			}
			val = str;
		}
		
		private function lexSymbol():void{
			var str:String = "";
			tok = TokenType.SYMBOL;
			
			while(position < length){
				var c:Number = reader.charCodeAt(position++);
				
				if(isNaN(c) == false){
					if(CharacterType.isIdentifierPart(c) == false){
						--position;
						break;
					}else{
						str += String.fromCharCode(c);
						//str += c.toString();
					}
				}else{
					throw new Error("isNaNがtrueでした");
				}
			}
			val = CodeSymbol.intern(str);
			
			if(reserved.hasOwnProperty(str) == true){
				tok = reserved[str];
			}
		}
		
		private function skipLineComment():void{
			try{
				while(position < length){
					var c:Number = reader.charCodeAt(position++);
					
					if(isNaN(c) == false){
						if(c == 0x0D){
							--position;
							break;
						}
					}else{
						throw new Error("isNaNがtrueでした");
					}
				}
			}catch(e:Error){
				throw e;
			}
		}
		private function skipComment():void{
			try{
				while(position < length){
					var c:Number = reader.charCodeAt(position++);
					
					if(isNaN(c) == false){
						if(c == CharacterType.MUL){
							c = reader.charCodeAt(position++);
							if(isNaN(c) == false){
								if(c == CharacterType.DEV){
									break;
								}
							}
						}
					}else{
						throw new Error("isNaNがtrueでした");
					}
				}
			}catch(e:Error){
				
			}
		}
		
		private function lexDigit():void{
			var num:Number = 0;
			
			try{
				while(position < length){
					var c:Number = reader.charCodeAt(position++);
					
					if(isNaN(c) == false){
						if(isDisit(c) == false){
							--position;
							break;
						}else{
							num = (num * 10) + (c - 0x30);
						}
					}else{
						throw new Error("isNaNがtrueでした");
					}
				}
				val = num;
			}catch(e:Error){
				
			}
		}
		
		private function isDisit(char:Number):Boolean{
			if(char < 0x30 || char > 0x39){
				return false;
			}
			return true;
		}
		
		private function skipWhiteSpace():void{
			try{
				while(position < length){
					var c:Number = reader.charCodeAt(position++);
					
					if(isNaN(c) == false){
						if(CharacterType.isWhileSpace(c) == false){
							--position;
							break;
						}
					}else{
						throw new Error("isNaNがtrueでした");
					}
				}
			}catch(e:Error){
				
			}
		}
	}
}